A.I. Wars Bug Development by Vusak (aka Simon Collins)

Download this Cybug for use with this tutorial:

Killme.ai

What do you mean I have to PROGRAM??!!!

What is #enemy_y or ~v5 meant to mean?

Okay you probably purchased this game knowing there is some programming involved, maybe you even know how to do a few things, but here is a brief outline of some of the conventions used in CAICL (bug language).

First of all are the values, values like #cur_ammo, #enemy_x, ~v2, ~vk. Each of these terms is like a package for a piece of information, the information can be a word "left", multiple words "turn left", a number "4", or a combination somehow "4 turn left 33". The terms that have a # in front of them are in the form of numbers only, while the terms with ~ are able to contain anything at all!

Another difference is when terms get their values, #cur_ammo indicates the current amount of ammunition your bug has, and so it is updated everytime you use ammo. This is the general nature of all # terms, they get updated automatically whenever there is a change of their source. The ~ terms only change when you tell it to, you must use them in a equation or "assign" them a value before they can hold one.

Term Type (with eg)

Values it can hold

When it gets those values

# (#shieldstatus)

Single Numbers

When its source changes (automatic)

~ (~v5)

Multiple/Single words and numbers

When your code tells it to (manual)

So what this means is, you have values that you can reference from to find out how things are at any point with the # terms and you also have values you can use to work things out (like algebra) with the ~ terms.

There are 2 ways to give a ~ term a value, the first is to use the "assign" command:

assign vg 4

or

assign vh cow

or even

assign v0 cow 4

And the other way is to use the equals sign and a equation:

cmath vg = 2 + 2

or

cmath v3 = #enemy_x – 6

or

cmath vt = #cur_life + 72 * ~v2

Notice something interesting here? When you give a value to a ~ term it doesn’t have the ~ yet, it is only used with a ~ in front when its being used, if its getting a value then you don’t use the ~ symbol (check the examples above).

  

If Then; how to make your bug choose

The next major idea involves the use of If Then statements, these are basically sentences that look like this:

If (this is true) Then (do this action)

So as you can see its pretty damn easy, the tricky part is more in how you are meant to write it so it doesn’t make errors. Here are some examples of statements that work and statements that don’t:

If ~v3 = 4 then goto fourmode ß This wont work because its missing the word "value"

If value ~v3 = 4 then goto fourmode ß This will work

If value #scan then cmath vj = 3/~v8 ß This is missing a comparison, If #scan is ___ then do this

If value #scan > 0 then cmath vj = 3/~v8 ß This is better, its comparing #scan to the number 8

You might have noticed the use of > and =. Hopefully you have a grasp of math and understand the equals sign and greater-than sign. There is also >=, <=, < as well. They allow you to check if a value is equal/greater/lesser, or some combination of those, in comparison to another value.

Also note that you do not ever need the "end if" statement in your code, simply forget that it even exists. And one last thing, you can combine if statements, for example:

If value ~v3 = 4 then if value #scan > 0 then goto fourmode scanned

This could go on quite a few times, with more checks and comparisons, it is limited by a character limit on the editor, but you wont ever need more than 5 checks anyway.

Labels, the easiest yet most important thing.

Simply put, a label is a marker with a unique name. You may want your bug to do something other than what is on the next line of code so you need to tell it to "goto" code somewhere else in the file. So you need 2 things, first a label and code to tell it to go to that label.

A label looks like this:

attack mode:

or

runlikehell:

or

close scan 2:

And to make your code "goto" one of these labels you would use something like this:

goto attack mode

or

goto runlikehell

or

goto close scan 2

you can also use other values like ~v5 and #cur_life in your goto statement:

goto close scan #scan

or

goto attack mode ~vr

This allows you to check for something like what a scan found and use it to go straight to the appropriate instructions.

 

Use what we’ve learned so far:

Start:

Assign v3 #cur_life

Scan perimeter

If value #scan > 0 then goto close scan #scan

Goto start

Close scan 2:

If value ~v3 >= 4 then goto runlikehell

Goto attackmode

Runlikehell:

Move forward

Move forward

Move forward

Goto start

Attackmode:

Discharge energy

Goto start

This is a fair bit to throw at you all at once but pretend you are the computer, go to the start label and read in order, first you make ~v3 hold the value of your health, the closer this number is to 10 the more damage your bug has. second you scan your perimeter, if you find something then the #scan value will be a number from 1 to 5. if its 2 then you have found an enemy and it will then goto close scan 2 otherwise it continues to the next line which tells it to go back to the start, if the value was 1,3,4,5 then it still goes onto the next line because there are no labels called "close scan 1", "close scan 3" etc. If it did end up detecting an enemy then it ends up on the line close scan 2, the next line then checks if ~v3 is above 4, remember that ~v3 was assigned the value of our #cur_life, so if our damage is equal to or above 4 then it will goto runlikehell, otherwise it will go onto attackmode. Take note that we could have just had the statement:

If value #cur_life >= 4 then goto runlikehell

Instead of the use of ~v3, this would be more current and also would allow us to cut out a line at the start.

Both runlikehell and attackmode then do something (the labels speak for themselves). You could put this into an actual bug, but you might find some problems running it, why? Because you need 3 lines at the start of every bug which look similar to this:

Name ScaredyCat

Author me

Iff code the wimps

Ofcourse you would use your own names for these but this allows you to both give your bug a name and also lets you take credit for the bugs performance, the other line "iff code" is short for "Is Friend or Foe" and is a unique name for your "team". Any bugs who have the exact same iff codes will treat each other as friends and will not attack each other (unless you disable iff codes in the options).

  

The Cybugging starts here

You have the basics, now its time to make the BUG!!!!

Please note that this is my way of programming, and so might not be the best way to do things, but on the other hand, it can help get the newbies started and might give the veterans some new ideas!!

First give your bug a mission, give your bug a specific aim or purpose. Obviously you will want it to be battle ready and able to kill every other bug, but How? You might be best going and having a look at how other bugs fight, what’s their main strategy? Are they defensive or aggressive, or do they change in different circumstances? Next, how do they use their strategy? If they’re defensive, do they wait till the enemy is in grenade range, or do they use mines to create a wall. If they’re aggressive, do they go straight for the kill with missiles firing, or do they change direction and try and sneak around the enemy? These tactics are used and more besides. Use the bug provided (Killme) as an example bug to practice with. Other bugs can be found at http://home.planet.nl/~ruit0036/. Observe how it attacks other bugs and identify the tactics it uses. Then see how other bugs defeat Killme, see what the weaknesses of it are and perhaps think of what the other bug does to exploit these weaknesses. It isn’t passworded so you can observe the actual mechanics of the bugs workings.

I’d advise you use a totally blank and open map to practice with.

You’ll first of all notice that it zigzags across the field directly towards its enemy (your bug), so you would have to assume it is rather aggressive, you might think "oh ok, ill just be MORE aggressive and blast the crap out of it!", you might well do this, but it will be tricky, fighting fire with fire is likely to work, bugs tend to work a little like a rock-paper-scissors type thing, although at any one time there is a bug in a tournament that might be kicking ass, its not too long before another bug uses a different tactic to beat it, by no means is this a fixed rule, just a common occurrence. So maybe instead of taking it head on think about what it does. You’ll notice it attacks as soon as it is in a line of fire, and that it also dodges out the way if its in danger and after it attacks. Dodging is a bit of a favourite tactic of mine, but I’ve noticed it loses its significance at closer ranges, simply because missiles and grenades move too fast, so they are very hard to dodge when they’ve been fired from short range. So maybe your bug would be better off getting close to Killme before attacking, this should save ammo and might waste the ammo of Killme too.

Goal update:

These goals are the main priorities of your bug so ideally they should be constantly monitored. High priorities usually reside in the first routine that is run, also they’re usually checked often so as a consequence, the first routine is often run the most, this isn’t necessarily the only way to structure you bug, it just seems intuitively right for me.

Killme zigzags across the field, this ensures that unless your bug does exactly the same thing that it will intercept your movement from a distance, so ideally we should aim to get close, but counteract Killme’s movements somehow.

  

So first put in the identification info:

name Firstbug

author Simon Collins

iff code mybugs

debug on

Obviously you can use your own names for these. Using debug allows you to test your bug, once you have finished creating it, make sure you delete the line.

To even get near an enemy you must first develop an equation that will work out which direction you should turn, so it should always output 3 values, a left value, a right value, and a straight value.

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)

+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

The above code when put on a single line in the ai file will produce the required results, and this is why:

|ß FIRST PART OF EQUATION à |

(#cur_head mod 2) * (#x_pos - #enemy_x) * (#cur_head - 2)

|ß First Sectionà | |ß Second Sectionà | |ß Third Sectionà |

It seems complicated, but it isn’t really, the First Section decides whether or not everything on this side of the + sign is 0 or a number, it does this by dividing your current heading (north = 1, east = 2, south = 3, west = 4) by 2, then if there is no remainder it will equal 0, if there is a remainder it will equal 1. This is the function of the MOD command, when 2 values are on either side of the mod command, it will equal 0 unless there is a remainder. So this first section will equal 0 if the bug is facing east (2) or west (4) because 2 divides into those numbers evenly. The rest of the FIRST PART OF EQUATION will only equal something other than zero if the bug is facing north or south. Now think about what we are trying to do, we are trying to find the direction of the enemy. The Second Section determines the distance, however note that it could be a negative number if our bug is to the right because our X-coordinate will be smaller than the enemies. This is useful, because so far that means that if the bug is facing north a negative number indicates the enemy is to the right, so a positive number means an enemy is to the left. This isn’t quite enough however, because what if the bug is facing south, to take care of this the Third Section provides either a 1 or –1 depending on whether the bug is facing north(1) or south(3), and this 1 or –1 will make sure that the final number will indicate the enemy is to the right(positive) or left(negative). Check the other part of the equation which has Y-coordinates instead of X. This allows the bug to work out which direction to go for when its facing east or west. There are 2 main difference, the first is the +1 added to the #cur_head value, this makes sure that any even (remainder 0) number is made into an odd number (remainder 1) and vice-versa. So this part will only work if the direction faced is east(2+1=3) or west (4+1=5). The second difference is the use of –3 at the end of the statement, this is because 2-3 and 4-3 will give us a 1 or –1.

And finally the SGN command at the start of the whole equation uses brackets to apply to the whole thing, this makes the final value only ever equal 1, 0, or –1.

 

That way you can then use a GOTO command like this:

goto turn ~v3

turn 1:

turn right

goto main

turn 0:

long range scan

if value #scan <> 2 then goto turnaround

launch missile

goto main

turn –1:

turn left

goto main

You could also do this with the GOSUB command:

gosub turn ~v3

goto main

turn 1:

turn right

return

turn 0:

long range scan

if value #scan <> 2 then gosub turnaround

launch missile

return

turn –1:

turn left

return

As you can see, what at first seems rather complex is easier to understand if you break it up into parts.

So far we can make our bug turn in the correct direction whenever we want it to, now we must work out how to make our bug get close, this can be done by monitoring the distance between our bug and the enemy at all times, sounds like another equation needs to be in the main routine:

cmath v2 = (abs(#enemy_x - #x_pos) < 6) AND (abs(#enemy_y - #y_pos) < 6)

|ß First Section à | |ß Second Section à |

First off, we find the distance between our X and the enemy’s X coordinate, if its 6 or less then the first section is TRUE, the same for the second section with Y coordinates. So if both conditions are TRUE then the whole equation will equal –1, if any one of them is FALSE then the equation will equal 0, even if both are FALSE, it will still be 0. So the next line would check to see if ~v2 is –1 or not.

if value ~v2 = -1 then goto huntmode

These 2 lines will only work if the enemy is within 6 spaces in both directions at the same time, this is good when we get to the enemy, but what if the enemy is on the same line but on the other side of the screen?

cmath v1 = (abs(#enemy_x - #x_pos) < 2) OR (abs(#enemy_y - #y_pos) < 2)

This equation will equal –1 if we are in 2 spaces of either direction, so with these 2 equations we can do this:

name Firstbug

author Simon Collins

iff code mybugs

debug on

main:

cmath v1 = (abs(#enemy_x - #x_pos) < 2) OR (abs(#enemy_y - #y_pos) < 2)

cmath v2 = (abs(#enemy_x - #x_pos) < 6) AND (abs(#enemy_y - #y_pos) < 6)

if value ~v1 = -1 then if value ~v2 <> -1 then move backward

if value ~v2 = -1 then goto huntmode

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 > 0 then turn right

if value ~v3 < 0 then turn left

move forward

goto main

huntmode:

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 > 0 then turn right

if value ~v3 < 0 then turn left

if value ~v3 = 0 then goto attackmode

move forward

goto main

attackmode:

scan forward

if value #scan = 2 then launch missile

evade:

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 > 0 then turn right

if value ~v3 < 0 then turn left

move backward

goto main

 

 

 

This bug will zigzag towards the enemy to try and get really close, if it gets in danger it will back away from the enemy’s line of fire.

If it gets in attacking range it zigzags straight for the enemy and then when it gets a clear shot it launches its missile (using the fire weapon command is effective at close range, but a little overkill never hurts), it then quickly ducks out the way and checks the main routine high priorities again. Notice that the attackmode leads straight onto the evade routine, this is because we want this bug to avoid return fire so we want it to evade straight after attacking, we also want it to evade if it doesn’t get a enemy in scanning range. That’s why it leads straight onto the evade routine. The reason why we don’t just make the evade routine part of the attackmode routine is because we want to be able to evade at other times too, so this structure works well for what we want. If you try this against the enemy you’ll find it doesn’t go so well, the main reason being that Killme has a little contingency plan for when it runs out of ammo (zapping!), so perhaps we should try a different weapon, grenades can be set to explode next to an enemy, so you don’t need a direct hit to be effective.

So lets review the goals:

 

 

 

X

So we need to first determine when an enemy is within grenade range, an enemy is in range if they’re on a line either the same as your own bugs or alongside it.

If your bug is the X then all the black areas can be attacked with your grenades. So the 2 main checks for grenade throwing are; make sure the enemy is atleast within 1 line adjacent, and work out how far away they are in the direction you’re facing. Our earlier formula of finding the direction of the enemy (turning equation) can be adapted to give a distance in the direction our bug is facing. Simply by changing the conditions so that it checks for X-distance when its facing along the X-axis and the same for the Y-values.

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)

+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

This above is the original, the one below is the distance measuring section of our grenade equation, the only differences are that the X and Y calculations have been swapped, and the SGN command has been removed. The SGN command reduces a number to either 1, 0, or –1 according to it being positive, 0, or negative. We want an actual number, so we leave it as such.

cmath v6 = (#cur_head mod 2)*(#enemy_y-#y_pos)*(#cur_head-2)

+(#cur_head+1) mod 2)*(#x_pos-#enemy_x)*(#cur_head-3)

The next thing we need is the component that checks if the enemy is alongside our axis, this could be factored into our huntmode routine, using another version of the turing formula, only this time, it is almost exactly the same as the original, only one differnce. It will not have any SGN command, it will have a ABS command, ie.

cmath v7 = abs((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)

+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

This will give us the distance crossways to our opponent, we want to know if its 1 space away, so positive and negative are irrelevant, so using the ABS command will mean we only need to check if it equals 1 to go to a grenade launching routine.

 

So now our bugcode looks a little like this:

name Firstbug

author Simon Collins

iff code mybugs

debug on

main:

cmath v1 = (abs(#enemy_x - #x_pos) < 2) OR (abs(#enemy_y - #y_pos) < 2)

cmath v2 = (abs(#enemy_x - #x_pos) < 6) AND (abs(#enemy_y - #y_pos) < 6)

if value ~v1 = -1 then if value ~v2 <> -1 then move backward

if value ~v2 = -1 then goto huntmode

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 > 0 then turn right

if value ~v3 < 0 then turn left

move forward

goto main

huntmode:

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 > 0 then turn right

if value ~v3 < 0 then turn left

cmath v7 = abs((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v7 = 1 then goto grenlauncher

scan forward

if value #scan = 2 then launch missile

if value #scan <> 2 then move forward

evade:

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 >= 0 then turn right

if value ~v3 < 0 then turn left

move backward

goto main

grenlauncher:

cmath v6 = (#cur_head mod 2)*(#enemy_y-#y_pos)*(#cur_head-2)+(#cur_head+1) mod 2)*(#x_pos-#enemy_x)*(#cur_head-3)

launch grenade

set grenade fuse ~v6

goto evade

 Now we have a bug that will destroy Killme about 80% of the time.

 

Seeking the Perfect Cybug

Before reading on, I’d suggest having a look at Killme’s bugcode (if you haven’t already), it contains a detailed explanation of how it works and why certain things are used. By now you should have a decent grasp of what’s going on, you almost certainly have heaps of ideas you’d like to try out, but there is one last thing to do with our bug, and that is probably one of the more significant steps, optimising. No matter how well you plan out your strategy, your bug will always have room for improvement. So we are going to have a look at what can be done to improve the previous examples’ code.

The first 2 equations ~v1 and ~v2 can be combined quite simply, here’s how:

cmath v1 = (abs(#enemy_x - #x_pos) < 2) OR (abs(#enemy_y - #y_pos) < 2)

cmath v2 = (abs(#enemy_x - #x_pos) < 6) AND (abs(#enemy_y - #y_pos) < 6)

becomes

cmath v1 = ((abs(#enemy_x - #x_pos) < 2) OR (abs(#enemy_y - #y_pos) < 2))

+((abs(#enemy_x - #x_pos) < 6) AND (abs(#enemy_y - #y_pos) < 6))

Now we have an equation that will give us a value of –1 if the enemy is on the next axis (like the ~v1 equation did) and it will answer –2 if the enemy is close enough for huntmode (like the ~v2 equation did).

So now the next if statement will be:

if value ~v1 = -1 then move backward

The next section would normally send us to huntmode if the enemy was close, or continue to the turning equation if not, however the first thing in the huntmode routine is the turning equation, so no matter what, the bug will always perform the turning equation, so why not put it before the huntmode command, ie.

cmath v3 = sgn((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)

+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v3 > 0 then turn right

if value ~v3 < 0 then turn left

if value ~v1 = -2 then goto huntmode

This saves lines, and should also make things easier to debug. The huntmode routine now looks like this:

cmath v7 = abs((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)

+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v7 = 1 then goto grenlauncher

scan forward

if value #scan = 2 then launch missile

if value #scan <> 2 then move forward

The thing is, the ~v7 equation also can tell us if the enemy is on the same line, not just adjacent, this is useful for missiles, so we can do this:

cmath v7 = abs((#cur_head mod 2)*(#x_pos-#enemy_x)*(#cur_head-2)

+((#cur_head+1) mod 2)*(#y_pos-#enemy_y)*(#cur_head-3))

if value ~v7 > 1 then goto evade ß we added a line in here; this line will make the bug faster for

if value ~v7 = 1 then goto grenlauncher when the enemy isn’t in line of fire, and it won’t make much

difference when the enemy is.

 

In Conclusion

The bug is now pretty much done, for the tutorial that is. However it could be quite easily turned into a tournament champ with some improvement here and there, I suggest you come up with an idea for a bug and work on it like you have here, break the big challenges into little steps and you can do wonders with A.I. Wars.

There are 3 more things you need to know:

If you have any questions, ideas, feedback, thankyous J , or whatever, feel free to send me an email at vusak@hotmail.com

You can also try the A.I. Wars forum where the community organizes tournaments, answers questions, discusses ideas, and is a good place to go for info.

Bugs can be downloaded from the A.I.Wars library at http://www.tacticalneuronics.com/CybugLibrary/CybugsAppBottomList.asp?Category=all

As well as Reiniers Tournament site at http://home.planet.nl/~ruit0036/